package cn.jasonone.jasonfx.util;

import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import lombok.extern.slf4j.XSlf4j;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 资源工具类
 *
 * @Author Jason
 * @Date 2020/1/18 20:51
 * @Version 1.0
 */

@Slf4j
public class Resources {
    private static final String CLASS_PATH_PREFIX = "classpath:";
    private static final String PROTOCOL_JAR = "jar";
    private static final String PROTOCOL_FILE = "file";
    private static final String SUFFIX_CLASS = ".class";
    private static final List<URL> CLASS_PATH_URLS = new ArrayList<>();

    static {
        try {
            Enumeration<URL> urls = ClassLoader.getSystemResources("");
            while (urls.hasMoreElements()) {
                CLASS_PATH_URLS.add(urls.nextElement());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据URI获取类名
     *
     * @param uri URI
     * @return 类名
     */
    private static String getClasssName(URI uri) {
        if (uri.toString().endsWith(SUFFIX_CLASS)) {
            for (URL url : CLASS_PATH_URLS) {
                try {
                    if (uri.toURL().getFile().startsWith(url.getFile())) {
                        String className = uri.toURL().getFile();
                        className=className.substring(url.getFile().length(), className.length()-6);
                        return className.replaceAll("/", ".");
                    }
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /**
     * 获得文件URL
     *
     * @param location
     * @return
     */
    public static URL getFile(String location) {
        return ClassLoader.getSystemResource(location);
    }

    /**
     * 扫描Jar包中的类
     *
     * @param url         Jar包的URL
     * @param packageName 基础包路径
     * @return 类集合
     */
    private static List<Class<?>> scanner(URL url, String packageName) {
        List<Class<?>> list = null;

        if (PROTOCOL_JAR.equals(url.getProtocol())) {
            try {
                String location = url.getFile();
                //转换路径:   file:/C:/a/b/c/d.jar!/e/f/g =>  C:/a/b/c/d.jar
                location = location.substring(6, location.indexOf('!'));
                JarFile jarFile = new JarFile(location);
                Enumeration<JarEntry> entries = jarFile.entries();
                while (entries.hasMoreElements()) {
                    JarEntry entry = entries.nextElement();
                    //得到类路径
                    String className = entry.getName();
                    //检测是否是类并且不是内部类
                    if (className.endsWith(SUFFIX_CLASS) && !className.contains("$")) {
                        //转化类全限定名称
                        // a/b/C.class => a.b.C
                        className = className.substring(0, className.lastIndexOf(".")).replaceAll("/", ".");
                        try {
                            Class<?> type = Class.forName(className);
                            // 检测是否属于包下
                            if (packageName == null || packageName != null && className.startsWith(packageName)) {
                                if (list == null) {
                                    list = new ArrayList<>();
                                }
                                list.add(type);
                            }
                        } catch (ClassNotFoundException e) {
                            log.error("类加载失败", e);
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return list == null || list.isEmpty() ? Collections.emptyList() : list;
    }

    /**
     * 扫描目录下的所有类
     *
     * @param parent      根目录
     * @param packageName 包名
     * @return 类集合
     */
    private static List<Class<?>> scanner(File parent, String packageName) {
        List<Class<?>> list = new ArrayList<>();
        if (parent == null) {
            return Collections.emptyList();
        } else {
            if (parent.isDirectory()) {
                File[] files = parent.listFiles();
                for (File f : files) {
                    list.addAll(scanner(f, packageName));
                }
            } else {
                try {
                    String className = getClasssName(parent.toURI());

                    if (className != null) {
                        Class<?> type = Class.forName(className);
                        if (StringUtil.isEmpty(packageName) || StringUtil.isNotEmpty(packageName) && type.getPackage() != null && type.getPackage().getName().startsWith(packageName)) {
                            list.add(type);
                        }

                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
        return list.isEmpty() ? Collections.emptyList() : list;
    }

    public static List<Class<?>> scaner(@NonNull String packageName) {
        List<Class<?>> list = new ArrayList<>();
        try {
            String rootLocation = packageName == null ? "" : packageName.replaceAll("\\.", "/");
            Enumeration<URL> urls = ClassLoader.getSystemResources(rootLocation);
            rootLocation = ClassLoader.getSystemResource("").getFile();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                if (PROTOCOL_JAR.equals(url.getProtocol())) {
                    //从Jar包中查找资源
                    list.addAll(scanner(url, packageName));
                } else if (PROTOCOL_FILE.equals(url.getProtocol())) {
                    //从系统文件中查找资源
                    File file = new File(url.toURI());
                    list.addAll(scanner(file, packageName));
                }
            }
        } catch (IOException | URISyntaxException e) {
            e.printStackTrace();
        }
        return list.isEmpty() ? Collections.emptyList() : list;
    }
}
